use std::io;
use std::fs;
use std::io::BufRead;
use regex::Regex;
use calamine::{Reader};


#[derive(Debug,Clone)]
pub  struct Loc{
    pub ls : i32,
    pub le :i32,
    pub rs:i32,
    pub re :i32,
    pub re_string:String,
    pub split_by:SplitBy,
    pub replace_to:String,
    pub mode: i32,
    pub point_to_loc: i32,
    pub grep_by_vec: Vec<String>,
    pub other_loc: Option<Box<Loc>>,
}

pub enum Value <'a>{
    Single(& 'a str),
    Multi(& 'a[calamine::DataType])
}


pub fn handle_each_line<F>(each: F,loc :Loc)
where F: Fn(i32, &str, Option<&Regex>,&Loc)
{
    
    let handler = io::stdin();
    // let mut stdlines:Vec<String>=  Vec::new();
    let mut lineno = 0;
    let re = match Regex::new(&loc.re_string){
        Ok(r) =>{
            Some(r)
        },
        Err(e) => {
            println!("error compile {:?} || {}",loc.re_string, e.to_string());
            panic!(e)
        }
    };
    loop{
        let mut line = String::new(); 
        match handler.read_line(&mut line) { 
            Ok(n) => {
                if n == 0{
                    break;
                }
                if loc.ls == -1{
                    if let Some(r) = &re{
                        each(lineno, &line.trim(), Some(&r), &loc);    
                    }else{
                        each(lineno, &line.trim(), None, &loc);
                    }
                    
                }else{
                    if lineno >= loc.ls && loc.le != -1 && lineno < loc.le{
                        
                        if let Some(r) = &re{
                            each(lineno, &line.trim(), Some(&r), &loc);    
                        }else{
                            each(lineno, &line.trim(), None, &loc);
                        }
                    }else if lineno >= loc.ls && loc.le == -1 {
                            
                        if let Some(r) = &re{
                            each(lineno, &line.trim(), Some(&r), &loc);    
                        }else{
                            each(lineno, &line.trim(), None, &loc);
                        }
                    }else if  loc.le != -1 && lineno > loc.le {
                        break
                    }
                }
                lineno+=1;
            },
            Err(e) => {
                println!("{}",e);
                break;
            }
        }
    };
    
}

pub fn handle_each_line_file<F>(path :&str,each:F, loc :Loc) -> io::Result<()>
where F:Fn(i32, &str, Option<&Regex>,&Loc){
    let f =  fs::File::open(path)?;
    let buffer = io::BufReader::new(f);
    // let mut lineno = 0;
    
    let re = match loc.mode {
        crate::MODE_EXTRACT | crate::MODE_DEL|crate::MODE_REPLACE => {
            Some(Regex::new(&loc.re_string).expect("compile error!"))
        },
        _ => None
    };

    for (i,l) in buffer.lines().enumerate(){
        match l{
            Ok(line) => {
                if loc.ls == -1{
                    if let Some(r) = &re{
                        each(i as i32, &line, Some(&r), &loc);
                    }else{
                        each(i as i32, &line, None, &loc);
                    }
                    
                }else{
                    if i as i32 >= loc.ls && loc.le != -1  && (i as i32) < loc.le{
                        if let Some(r) = &re{
                            each(i as i32, &line, Some(&r), &loc);
                        }else{
                            each(i as i32, &line, None, &loc);
                        }
                    }else if i as i32 >= loc.ls && loc.le == -1 {
                        if let Some(r) = &re{
                            each(i as i32, &line, Some(&r), &loc);
                        }else{
                            each(i as i32, &line, None, &loc);
                        }
                    }else if  loc.le != -1 && (i as i32) > loc.le {
                        break
                    }
                }
                
            },
            Err(_) => {
                break
            } 
            // None(_) => {}
        }
    }
    
    // loop{
    //     let mut line = String::new();
    //     match buffer.read_line(&mut line){
    //         Err(_) =>  {
    //             break
    //         },
    //         Ok(s) => {
    //             if s> 0{
    //                 each(lineno, &line);
    //                 lineno+=1;
    //             }
    //         }
    //     }
    // }
    Ok(())
}

#[warn(dead_code)]
#[derive(Debug,Clone)]
pub enum SplitBy {
    Direct(String),
    ReSplit(Regex),
    No(i32),
}



impl Loc{
    pub fn new() -> Self{
        Self{
            ls:-1,
            le:-1,
            rs:0,
            re:-1,
            point_to_loc:0,
            re_string:"".to_owned(),
            split_by: SplitBy::Direct("{SPACE}".to_owned()),
            replace_to:"".to_owned(),
            mode:-1,
            other_loc:None,
            grep_by_vec:vec![],
        }
    }
    pub fn set_mode(&mut self, mode:i32) -> &Self{
        if self.point_to_loc == 0{
            self.mode = mode;
        }else{
            if let Some(s) = &mut self.other_loc{
                s.mode = mode;
            };
        }
        self
    }

    // #[dead_code]
    #[warn(dead_code)]
    pub fn parse(arg: &str) -> Loc{
        let mut loc = Loc{
            ls:0,
            le:-1,
            rs:0,
            re:-1,
            point_to_loc:0,
            re_string:"".to_owned(),
            split_by: SplitBy::No(-1),
            mode:-1,
            replace_to:"".to_owned(),
            other_loc:None,
            grep_by_vec:vec![],
        };

        if arg.contains(","){
            let args = arg.splitn(2, ",").collect::<Vec<&str>>();
            loc.set_rows(args[0]);
            loc.set_columns(args[0]);
            
        }else{
            loc.set_rows(arg);
        }

        loc
    }

    pub fn filter(&self, line :&str) -> Option<String>{
        // let values = vec![];
        match self.mode{
            crate::MODE_GREEP =>{
                if !line.contains(&self.re_string){
                    return None;
                }
            },
            _ => {
            }
        };
        if let Some(strs) = self.split(line){
            if self.re < strs.len() as i32 && self.re != -1{
                Some(strs[self.rs as usize ..self.re as usize].join(" "))
            }else{
                Some(strs[self.rs as usize ..].join(" "))
            }
        }else{
            Some(line.to_owned())
        }
    } 

    pub fn collect_grep_by(&mut self) -> &mut Self{
        let name = self.re_string.clone();
        if let Some(other_loc) = &mut self.other_loc{
            if name.ends_with(".xlsx"){
                let mut workbook = calamine::open_workbook_auto(name).expect("Cannot open file");
                let names = workbook.sheet_names();
                let sh_name = names[0].clone();
                if let Some(Ok(range)) = workbook.worksheet_range(&sh_name) {
                    other_loc.le  = range.get_size().0 as i32;
                    let width = range.get_size().1 as i32;
                    // loc.le = range.get_size().1 as i32;
                    // println!("{} {} {:?}", name, sh_name,loc);

                    self.grep_by_vec =  range.rows().filter(|cols|{
                        match other_loc.mode{
                            crate::MODE_GREEP =>{
                                format!("{:?}", cols).contains(&other_loc.re_string)
                            },
                            _ => {
                                true
                            }
                        }
                    }).map(|cols|{
                        // println!("{:?}", other_loc);
                        if other_loc.re < width && other_loc.re != -1{
                            cols[other_loc.rs as usize ..other_loc.re as usize].iter().map(|x| format!("{}",x)).collect::<Vec<String>>().join(",")
                        }else{
                            cols[other_loc.rs as usize..width as usize].iter().map(|x| format!("{}",x)).collect::<Vec<String>>().join(",")
                        }
                    }).collect();
                        
                    // atively, we can manually filter rows
                    // assert_eq!(non_empty_cells, range.rows()
                        // .flat_map(|r| r.iter().filter(|&c| c != &DataType::Empty)).count());
                }
                
            }else{
                let f =  fs::File::open(name).expect("msg: &str");
                let buffer = io::BufReader::new(f);

                self.grep_by_vec  = buffer.lines().map(|l| 
                        if let Ok(li) = l{       
                            if let Some(out) = other_loc.filter(&li.clone()){
                                out
                            }else{
                                li.clone()
                            }
                        }else{
                            "[NULL]".to_owned()
                        }
                
                ).collect::<Vec<String>>()
            }
        }
        return self
    }

    pub fn set_loc(&mut self,arg: &str) -> &mut Self{
        
        if arg.contains(","){
            let args = arg.splitn(2, ",").collect::<Vec<&str>>();
            self.set_rows(args[0]);
            self.set_columns(args[0]);
            
        }else{
            self.set_rows(arg);
        }

        self
    }

    pub fn set_rows(&mut self, arg:&str) -> &mut Self{
        if self.point_to_loc > 0{
            if let Some(oloc) = &mut self.other_loc{
                oloc.set_rows(arg);
            }
            return self
        };
        if arg.contains(":"){
            let ss :Vec<&str> = arg.splitn(2,":").collect();
            if ss[0] ==""{
                self.ls = 0
            }else{
                self.ls = ss[0].parse::<i32>().expect("parse string to int error ");
            }
            if ss[1] == ""{
                self.le = -1
            }else{
                self.le = ss[1].parse::<i32>().expect("parse string to int error ");
            }
        }else{
            self.ls = arg.parse::<i32>().expect("parse string to int error ");
            self.le = self.ls+1;
        }
        return self
    }

    pub fn set_columns(&mut self, arg:&str) -> &mut Self{
        if self.point_to_loc > 0{
            if let Some(oloc) = &mut self.other_loc{
                oloc.set_columns(arg);
            }
            return self
        };
        if arg.contains(":"){
            let ss :Vec<&str> = arg.splitn(2,":").collect();
            if ss[0] == ""{
                self.rs = 0
            }else{
                self.rs = ss[0].parse::<i32>().expect("parse string to int error ");
            }
            if ss[1] == ""{
                self.re = -1
            }else{
                self.re = ss[1].parse::<i32>().expect("parse string to int error ");
            }
        
        }else{
            self.rs = arg.parse::<i32>().expect("parse string to int error ");
            self.re = self.rs+1;
        }
        return self
    }

    pub fn split<'a>(&self, line:&'a str) -> Option<Vec<&'a str>>{
        let fs: Vec<&str> =  match &self.split_by{
            SplitBy::Direct(dirsplit) => {
                if dirsplit.eq("{SPACE}"){
                    line.split_whitespace().collect()    
                }else{
                    line.split(dirsplit).collect()
                }
            },
            SplitBy::ReSplit(rsplit) =>{
                rsplit.split(&line).collect()
            },
            SplitBy::No(_) => {
                return None
            }
        };
        Some(fs)
    }

    pub fn set_split_by(&mut self,split_str: &str) -> &mut Self{
        if self.point_to_loc > 0{
            if let Some(oloc) = &mut self.other_loc{
                oloc.set_split_by(split_str);
            }
            return self
        };
        if split_str.starts_with("r#"){
            let re = match Regex::new(&split_str[2..]){
                Ok(r) =>{
                    r
                },
                Err(e) => {
                    println!("error compile {:?} || {}",split_str, e.to_string());
                    panic!(e)
                }
            };
            self.split_by = SplitBy::ReSplit(re);
        }else{
            self.split_by = SplitBy::Direct(split_str.to_owned());
        }
        self
    }


    pub fn show_line_or_values(&self, l :Value) -> &Self{
        
        if self.mode == crate::MODE_GREEP {
            match l{
                Value::Single(line) =>{
                    if line.contains(&self.re_string){
                        if self.re == self.rs && self.rs == -1{
                            println!("{}", line);
                            return self
                        }
                        let fs: Vec<&str> =  match &self.split_by{
                            SplitBy::Direct(dirsplit) => {
                                if dirsplit.eq("{SPACE}"){
                                    line.split_whitespace().collect()    
                                }else{
                                    line.split(dirsplit).collect()
                                }
                            },
                            SplitBy::ReSplit(rsplit) =>{
                                rsplit.split(&line).collect()
                            },
                            SplitBy::No(_) => {
                                println!("{}",&line);
                                return self
                            }
                        };
                        let start_col = if self.rs == -1{
                            0
                        }else{
                            self.rs
                        };
                        
                        let newline = if self.re == -1 || self.re >= fs.len() as i32{
                            fs.get(start_col as usize..)
                        }else{
                            fs.get(start_col as usize ..self.re as usize)
                        }.expect(&format!("slice columns failed!! || \nfs: {:?}\nrs/re : {}/{}\n",fs,self.rs, self.re));
                        println!("{}",newline.join(","));      
                    }
                },
                Value::Multi(values) =>{
                    // println!("loc :{:?}",self);
                    if format!("{:?}", values).contains(&self.re_string){
                        if self.re == self.rs && self.rs == -1{
                            for i in values{
                                print!("{} ", i)
                            }
                            print!("\n");
                            return self
                        }
                        
                        let start_col = if self.rs == -1{
                            0
                        }else{
                            self.rs
                        };
                        
                        for i in &values[start_col as usize ..self.re as usize]{
                            print!("{} ", i)
                        }
                        print!("\n");
                    }
                }
            }
            
            return self
            
        }
        match l{
            Value::Single(line) =>{
                if self.re == self.rs && self.rs == -1{
                    println!("{}", line);
                    return self;
                }
                let fs: Vec<&str> =  match &self.split_by{
                    SplitBy::Direct(dirsplit) => {
                        if dirsplit.eq("{SPACE}"){
                            line.split_whitespace().collect()    
                        }else{
                            line.split(dirsplit).collect()
                        }
                    },
                    SplitBy::ReSplit(rsplit) =>{
                        rsplit.split(&line).collect()
                    },
                    SplitBy::No(_) => {
                        println!("{}",&line);
                        return self
                    }
                };
                let start_col = if self.rs == -1{
                    0
                }else{
                    self.rs
                };
                
                let newline = if self.re == -1 || self.re >= fs.len() as i32 {
                    fs.get(start_col as usize..)
                }else{
                    fs.get(start_col as usize ..self.re as usize)
                }.expect(&format!("slice columns failed!! || \nfs: {:?}\nrs/re : {}/{}\n",fs,self.rs, self.re));
                println!("{}",newline.join(","));
            },
            Value::Multi(values) =>{
                if self.re == self.rs && self.rs == -1{
                    for i in values{
                        print!("{} ", i)
                    }
                    print!("\n");
                    return self;
                }else{
                    // for (no,value) in values.enumerate(){

                    // }
                    if self.re < values.len() as i32{
                        for i in &values[self.rs as usize ..self.re as usize]{
                            print!("{} ", i)
                        }
                        print!("\n");
                
                    }else{
                        for i in &values[self.rs as usize ..]{
                            print!("{} ", i)
                        }
                        print!("\n"); 
                    }
                }
                
            }
        };
        return self
    }
}

